#pragma once

#include "Cyclic/CyclicCurves3.h"
#include "../Core/Matrices.h"
#include "../Core/DCoordinates3.h"
#include "../Core/GenericCurves3.h"
#include "../Core/Constants.h"
#include "../Core/Exceptions.h"
#include <stdlib.h>

namespace cagd
{
    class CyclicCurvePopulation {

    public:
        // Nested class
        class Individual {
            friend class CyclicCurvePopulation;
        protected:

            GLuint _n;
            ColumnMatrix<DCoordinate3> _data_point_to_interpolate;
            ColumnMatrix<GLdouble> _knots_vector; // csomopontok
            GLuint _max_order_of_derivatives;
            GLuint _div_point_count;

            CyclicCurve3 *_individual;

            GLdouble _fitness;
            GenericCurve3 *_image;

        public:
            Individual(GLuint n, ColumnMatrix<DCoordinate3>& data_point_to_interpolate, GLuint max_order_of_derivatives = 2, GLuint div_point_count = 100);
            Individual(GLuint n, ColumnMatrix<DCoordinate3>& data_point_to_interpolate, ColumnMatrix<GLdouble> knot_vector, GLuint max_order_of_derivatives = 2, GLuint div_point_count = 100);
            GLboolean randomizeKnots(GLuint number_of_knots);
            GLboolean calculateLength();
            GLvoid render();

            ColumnMatrix<GLdouble> getKnotsVector() {
                return _knots_vector;
            }

            GLdouble getFitness() {
                return _fitness;
            }

            GenericCurve3* getImage() {
                return _image;
            }

            CyclicCurve3* getCurve() {
                return _individual;
            }

            ~Individual() {
                cout << "destructor Ind\n";
                if (_individual) {
                    delete _individual; _individual = 0;
                }
                if (_image) {
                    delete _image; _image = 0;
                }
            }
        };

    protected:
        GLdouble r_max;
        GLuint _n;
        ColumnMatrix<DCoordinate3> _data_point_to_interpolate;
        GLuint _iterationBetweenGeneration;

        GLuint _number_of_individuals;
        RowMatrix<Individual*> _individuals;

//        RowMatrix<GLuint> _selection;
//        GLuint _selection_size;

    public:


        CyclicCurvePopulation(GLuint number_of_individuals, GLuint n, ColumnMatrix<DCoordinate3> data_point_to_interpolate, GLuint iterationBetweenGeneration = 1, GLuint max_order_of_derivatives = 2, GLuint div_point_count = 100);
        Individual* getBest();
        RowMatrix<Individual*> getAllIndividual();

        GLvoid sort();
        ColumnMatrix<GLdouble> sortKnots(ColumnMatrix<GLdouble> knots_vector);
        Individual* selection(RowMatrix<Individual*> _individuals);
        GLboolean mutation();
        GLboolean recombination();
        GLvoid searchPlace(Individual* best_parent, Individual* worst_parent, Individual* child);
        GLvoid evolve();

        ~CyclicCurvePopulation() {
            cout << "destructor Pop\n";
            for (GLuint i = 0; i < _individuals.GetColumnCount(); i++) {
                if (_individuals[i]) {
                    delete _individuals[i]; _individuals[i] = 0;
                }
            }
        }

    };
}


/* homework
felosztas: 2*PI / 2n+1 <= Aj
class CyclicCurvePopulation
{
     public inner class Individual {
         CyclicCurve * _individual;
         RowMatrix<GLdouble> knots; // csomopontok - mikor menjen at a csomoponton
         GLdouble _fitness;
         GenericCurve
     }
     protected: RowMatrix<DCoordinate3> data_point_to_interpolate;
     Selection, Evolve, Mutation, Recombination
     Generalas: Uj = Uj + rnd * (Uj+1 - Uj); // rnd [-1 1];
}
*/
